// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 此文件实现用于范围位置计算的辅助函数。

package syntax

// StartPos返回n的起始位置。
func StartPos(n Node) Pos {
	// 注释掉不需要更正的节点的情况。
	for m := n; ; {
		switch n := m.(type) {
		case nil:
			panic("internal error: nil")

		// 包装
		case *File:
			// 文件块从文件的开头开始
			return MakePos(n.Pos().Base(), 1, 1)

		// 声明
		// 案例*ImportDecl：
		// 案例*ConstDecl：
		// 案例*TypeDecl：
		// 案例*VarDecl：
		// 案例*FuncDecl：

		// 表达
		// 案例*BadExpr：
		// 案例*名称：
		// 案例*BasicLit：
		case *CompositeLit:
			if n.Type != nil {
				m = n.Type
				continue
			}
			return n.Pos()
		// 案例*KeyValueExpr：
		// 案例*FuncLit：
		// 案例*ParenExpr：
		case *SelectorExpr:
			m = n.X
		case *IndexExpr:
			m = n.X
		// 案例*slicexpr：
		case *AssertExpr:
			m = n.X
		case *TypeSwitchGuard:
			if n.Lhs != nil {
				m = n.Lhs
				continue
			}
			m = n.X
		case *Operation:
			if n.Y != nil {
				m = n.X
				continue
			}
			return n.Pos()
		case *CallExpr:
			m = n.Fun
		case *ListExpr:
			if len(n.ElemList) > 0 {
				m = n.ElemList[0]
				continue
			}
			return n.Pos()
		// 类型
		// 案例*阵列类型：
		// 案例*类型：
		// case*DotsType：
		// 案例*结构类型：
		// 案例*字段：
		// case*接口类型：
		// 案例*类型：
		// 案例*映射类型：
		// 案例*ChanType：

		// 声明
		// 案例*EmptyStmt：
		// 案例*LabeledStmt：
		// 案例*BlockStmt：
		// 案例*ExprStmt：
		case *SendStmt:
			m = n.Chan
		// 案例*DeclStmt：
		case *AssignStmt:
			m = n.Lhs
		// 案例*BranchStmt：
		// 案例*CallStmt：
		// 案例*ReturnStmt：
		// 案例*IfStmt：
		// 案例*ForStmt：
		// 案例*SwitchStmt：
		// 案例*SelectStmt：

		// 辅助节点
		case *RangeClause:
			if n.Lhs != nil {
				m = n.Lhs
				continue
			}
			m = n.X
		// 案例*案例条款：
		// 案例*第条：

		default:
			return n.Pos()
		}
	}
}

// EndPos返回源中n的近似结束位置。
// 对于某些节点（*Name，*BasicLit），它会立即返回位置
// 跟随节点；对于其他（*BlockStmt、*SwitchStmt等）它
// 返回结束“}”的位置；对于一些（*ParenExpr）
// 返回的位置是最后一个封闭对象的结束位置
// 表示
// 因此，EndPos不应用于精确标定
// 源中节点的结尾；最有用的是确定
// 有一定余地的范围。
func EndPos(n Node) Pos {
	for m := n; ; {
		switch n := m.(type) {
		case nil:
			panic("internal error: nil")

		// 包装
		case *File:
			return n.EOF

		// 声明
		case *ImportDecl:
			m = n.Path
		case *ConstDecl:
			if n.Values != nil {
				m = n.Values
				continue
			}
			if n.Type != nil {
				m = n.Type
				continue
			}
			if l := len(n.NameList); l > 0 {
				m = n.NameList[l-1]
				continue
			}
			return n.Pos()
		case *TypeDecl:
			m = n.Type
		case *VarDecl:
			if n.Values != nil {
				m = n.Values
				continue
			}
			if n.Type != nil {
				m = n.Type
				continue
			}
			if l := len(n.NameList); l > 0 {
				m = n.NameList[l-1]
				continue
			}
			return n.Pos()
		case *FuncDecl:
			if n.Body != nil {
				m = n.Body
				continue
			}
			m = n.Type

		// 表达
		case *BadExpr:
			return n.Pos()
		case *Name:
			p := n.Pos()
			return MakePos(p.Base(), p.Line(), p.Col()+uint(len(n.Value)))
		case *BasicLit:
			p := n.Pos()
			return MakePos(p.Base(), p.Line(), p.Col()+uint(len(n.Value)))
		case *CompositeLit:
			return n.Rbrace
		case *KeyValueExpr:
			m = n.Value
		case *FuncLit:
			m = n.Body
		case *ParenExpr:
			m = n.X
		case *SelectorExpr:
			m = n.Sel
		case *IndexExpr:
			m = n.Index
		case *SliceExpr:
			for i := len(n.Index) - 1; i >= 0; i-- {
				if x := n.Index[i]; x != nil {
					m = x
					continue
				}
			}
			m = n.X
		case *AssertExpr:
			m = n.Type
		case *TypeSwitchGuard:
			m = n.X
		case *Operation:
			if n.Y != nil {
				m = n.Y
				continue
			}
			m = n.X
		case *CallExpr:
			if l := lastExpr(n.ArgList); l != nil {
				m = l
				continue
			}
			m = n.Fun
		case *ListExpr:
			if l := lastExpr(n.ElemList); l != nil {
				m = l
				continue
			}
			return n.Pos()

		// 类型
		case *ArrayType:
			m = n.Elem
		case *SliceType:
			m = n.Elem
		case *DotsType:
			m = n.Elem
		case *StructType:
			if l := lastField(n.FieldList); l != nil {
				m = l
				continue
			}
			return n.Pos()
			// TODO（gri）需要考虑标记列表
		case *Field:
			if n.Type != nil {
				m = n.Type
				continue
			}
			m = n.Name
		case *InterfaceType:
			if l := lastField(n.MethodList); l != nil {
				m = l
				continue
			}
			return n.Pos()
		case *FuncType:
			if l := lastField(n.ResultList); l != nil {
				m = l
				continue
			}
			if l := lastField(n.ParamList); l != nil {
				m = l
				continue
			}
			return n.Pos()
		case *MapType:
			m = n.Value
		case *ChanType:
			m = n.Elem

		// 声明
		case *EmptyStmt:
			return n.Pos()
		case *LabeledStmt:
			m = n.Stmt
		case *BlockStmt:
			return n.Rbrace
		case *ExprStmt:
			m = n.X
		case *SendStmt:
			m = n.Value
		case *DeclStmt:
			if l := lastDecl(n.DeclList); l != nil {
				m = l
				continue
			}
			return n.Pos()
		case *AssignStmt:
			m = n.Rhs
			if m == nil {
				p := EndPos(n.Lhs)
				return MakePos(p.Base(), p.Line(), p.Col()+2)
			}
		case *BranchStmt:
			if n.Label != nil {
				m = n.Label
				continue
			}
			return n.Pos()
		case *CallStmt:
			m = n.Call
		case *ReturnStmt:
			if n.Results != nil {
				m = n.Results
				continue
			}
			return n.Pos()
		case *IfStmt:
			if n.Else != nil {
				m = n.Else
				continue
			}
			m = n.Then
		case *ForStmt:
			m = n.Body
		case *SwitchStmt:
			return n.Rbrace
		case *SelectStmt:
			return n.Rbrace

		// 辅助节点
		case *RangeClause:
			m = n.X
		case *CaseClause:
			if l := lastStmt(n.Body); l != nil {
				m = l
				continue
			}
			return n.Colon
		case *CommClause:
			if l := lastStmt(n.Body); l != nil {
				m = l
				continue
			}
			return n.Colon

		default:
			return n.Pos()
		}
	}
}

func lastDecl(list []Decl) Decl {
	if l := len(list); l > 0 {
		return list[l-1]
	}
	return nil
}

func lastExpr(list []Expr) Expr {
	if l := len(list); l > 0 {
		return list[l-1]
	}
	return nil
}

func lastStmt(list []Stmt) Stmt {
	if l := len(list); l > 0 {
		return list[l-1]
	}
	return nil
}

func lastField(list []*Field) *Field {
	if l := len(list); l > 0 {
		return list[l-1]
	}
	return nil
}
